#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <fcntl.h>
#include <iostream>
#include <cstring>

#include "connection.h"

// ---------------------------------------------
bool Connection::establish(const std::string hostname, const std::string port, std::string *res)
// ---------------------------------------------
{
	int status;
	struct ::addrinfo hints;
	struct ::addrinfo *servinfo; // will point to the results
	std::memset(&hints, 0, sizeof(hints)); // make sure the struct is empty
	hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
	hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
	
	// get ready to connect
	status = ::getaddrinfo(hostname.data(), port.data(), &hints, &servinfo);
	if (status != 0) {
		std::cerr << "getaddrinfo(): " << ::gai_strerror(status) << std::endl;
		return false;
	}
	
	// loop through all the results and connect to the first we can
	sockfd_ = 0;
	struct addrinfo* p;
	for (p = servinfo; p != NULL; p = p->ai_next)
	{
		if ((sockfd_ = ::socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
		{
			sockfd_ = 0;
			continue;
		}
		if (::connect(sockfd_, p->ai_addr, p->ai_addrlen) == -1)
		{
			::close(sockfd_);
			sockfd_ = 0;
			continue;
		}
		break;
	}
	::freeaddrinfo(servinfo); // all done with this structure

	if (p == NULL) {
		std::cerr << "Failed to establish a connection to " << hostname << ":" << port << std::endl;
		return false;
	}
	
	// receive data
	if (res != NULL)
	{
		recieve(res);
	}
	return true;
}

// ---------------------------------------------
void Connection::close()
// ---------------------------------------------
{
	// check if the user establish()ed first
	if (sockfd_ != 0)
	{
		::close(sockfd_);
	}
}

// ---------------------------------------------
bool Connection::isOpen()
// ---------------------------------------------
{
	return (sockfd_ != 0);
}

// ---------------------------------------------
bool Connection::sendrecv(const std::string message, std::string *res)
// ---------------------------------------------
{
	// check if the user establish()ed first
	if (sockfd_ == 0) {
		return false;
	}

	int bytes_sent = 0;
	int len = message.size();
	int status;
	while (len > bytes_sent)
	{
		if ((status = ::send(sockfd_, &message[bytes_sent], len-bytes_sent, 0)) != -1)
		{
			bytes_sent += status;
		}
		else
		{
			return false;
		}
	}

	if (res != NULL) {
		recieve(res);
	}
	return true;
}

// ---------------------------------------------
void Connection::recieve(std::string *res)
// ---------------------------------------------
{
	char msg[512];
	int status = 1;

	while (status > 0)
	{
		for (unsigned short int i=0; i<512; ++i) msg[i] = 0;
		
		if ((status = recv(sockfd_, msg, 511, 0)) != -1)
		{
			// zero after the recv'd data
			msg[status] = 0;
			res->append(msg);
			::fcntl(sockfd_, F_SETFL, O_NONBLOCK); // set socket to nonblocking
		}
		else
		{
			::fcntl(sockfd_, F_SETFL, 0);
		}
	}
	::fcntl(sockfd_, F_SETFL, 0);
}

